package engine

import (
	"bytes"
	"io"
	"net/http"
	"net/url"
	"strings"
)

type RequestModifier interface {
	Modify(*http.Request) error
}

type HeaderModifier struct {
	headers map[string]string
}

func NewHeaderModifier(headers map[string]string) *HeaderModifier {
	return &HeaderModifier{headers: headers}
}

func (h *HeaderModifier) Modify(r *http.Request) error {
	for k, v := range h.headers {
		r.Header.Set(k, v)
	}
	return nil
}

type URLModifier struct {
	backend *url.URL
}

func NewURLModifier(backend *url.URL) *URLModifier {
	return &URLModifier{backend: backend}
}

func (u *URLModifier) Modify(r *http.Request) error {
	r.URL.Scheme = u.backend.Scheme
	r.URL.Host = u.backend.Host

	// 如果后端URL包含路径，需要正确处理路径拼接
	if u.backend.Path != "" {
		if !strings.HasSuffix(u.backend.Path, "/") {
			r.URL.Path = u.backend.Path + r.URL.Path
		} else {
			r.URL.Path = u.backend.Path + strings.TrimPrefix(r.URL.Path, "/")
		}
	}

	return nil
}

type BodyModifier struct {
	modifier func([]byte) ([]byte, error)
}

func NewBodyModifier(modifier func([]byte) ([]byte, error)) *BodyModifier {
	return &BodyModifier{modifier: modifier}
}

func (b *BodyModifier) Modify(r *http.Request) error {
	if r.Body == nil {
		return nil
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		return err
	}
	r.Body.Close()

	modifiedBody, err := b.modifier(body)
	if err != nil {
		return err
	}

	r.Body = io.NopCloser(bytes.NewReader(modifiedBody))
	r.ContentLength = int64(len(modifiedBody))

	return nil
}

type RequestModifierChain struct {
	modifiers []RequestModifier
}

func NewRequestModifierChain(modifiers ...RequestModifier) *RequestModifierChain {
	return &RequestModifierChain{modifiers: modifiers}
}

func (c *RequestModifierChain) Modify(r *http.Request) error {
	for _, modifier := range c.modifiers {
		if err := modifier.Modify(r); err != nil {
			return err
		}
	}
	return nil
}

// CloneRequest creates a deep copy of the request
func CloneRequest(r *http.Request) (*http.Request, error) {
	clone := new(http.Request)
	*clone = *r

	if r.Body != nil {
		body, err := io.ReadAll(r.Body)
		if err != nil {
			return nil, err
		}
		r.Body.Close()

		clone.Body = io.NopCloser(bytes.NewReader(body))
		r.Body = io.NopCloser(bytes.NewReader(body))
	}

	if r.URL != nil {
		clone.URL = new(url.URL)
		*clone.URL = *r.URL
	}

	if r.Header != nil {
		clone.Header = make(http.Header)
		for k, v := range r.Header {
			clone.Header[k] = append([]string{}, v...)
		}
	}

	if r.Trailer != nil {
		clone.Trailer = make(http.Header)
		for k, v := range r.Trailer {
			clone.Trailer[k] = append([]string{}, v...)
		}
	}

	return clone, nil
}

// ForwardRequest forwards the request to a backend
func ForwardRequest(client *http.Client, r *http.Request, modifiers ...RequestModifier) (*http.Response, error) {
	req, err := CloneRequest(r)
	if err != nil {
		return nil, err
	}

	chain := NewRequestModifierChain(modifiers...)
	if err := chain.Modify(req); err != nil {
		return nil, err
	}

	return client.Do(req)
}
